module("lgx.gens.luabind.module", package.seeall)
local lbind       = require'lgx.gens.luabind'
local strbuf      = require'lgx.strbuf'
local decl        = require'lgx.decl'
local utils       = require'lgx.utils'
local codefile    = require'lgx.codefile'
local concatpath  = utils.concatpath
local filecleaner = utils.filecleaner
local format      = string.format
local luagccxml   = require'lgx.gens.luabind.luagccxml'
local mt          = {}
mt.__index        = mt

function new(name, codeset, depmod)
   assert(type(name) == 'string', 'argument "name" must be a string')
   local o = {}
   o.codeset = codeset
   o.depmod  = depmod
   o.name    = name
   --more data member can be added below

   --
   setmetatable(o, mt)
   return o
end


local function header(fname,mod)
    local buf = strbuf.new()
    local template =
[[#ifndef $(modname)_h_
#define $(modname)_h_
$(header)
void luagccxml_$(modname)_open(lua_State *L);
#endif
]]
    local repmap = {
        ['$(header)'] = lbind.HEADER,
        ['$(modname)'] = mod.name
    }
    buf = buf + utils.template(template, repmap)
    return tostring(buf)
end

local function source(fname, mod)
    local template =
[[
$(fheader)
$(geninclude)
namespace $(lgxns){
$(wrapper)
}
void luagccxml_$(modname)_open(lua_State *L)
{
    $(ns)module(L, "$(modname)")
    [$(gencode)
    ];
}
]]
    local gencode = strbuf.new()
    local wrapper = strbuf.new()
    local incs = {}
    local incbuf = strbuf.new()
    local function handler(set, cell)
        if cell.ignore then return end
        local code, wrap = cell:gencode()
        gencode = gencode + code
        wrapper = wrapper + wrap
        local declfile = cell:declfile()
        if declfile and not incs[declfile] then
            incbuf = incbuf + ('#include <'..declfile..'>')
            incs[declfile] = true
        end
    end
    mod.codeset:traverse(handler)

    --make include file code
    local repmap = {
        ['$(fheader)'] = '#include "'..fname..'.h"',
        ['$(modname)'] = mod.name,
        ['$(geninclude)'] = #incbuf > 0 and incbuf:concat('\n')..'\n' or '',
        ['$(wrapper)'] = tostring(wrapper),
        ['$(gencode)'] = string.gsub(gencode:concat(',\n'), '\n', '\n'..string.rep('    ', lbind.DEFINDENT)),
        ['$(lgxns)'] = 'luagccxml',
        ['$(ns)'] = lbind.ns(),
    }
    return utils.template(template, repmap)
end

function mt:contained(cell)
    return self.codeset:contained(cell)
end

------------------------------------------------------------------------
--  generate .h an .cpp for one class
------------------------------------------------------------------------
local function writeclass(fc, path, cell)
    local htemplate = 
[[#ifndef $(macro)
#define $(macro)
#include <luabind/luabind.hpp>
namespace $(wpns){
$(funcdecl);
}
#endif
]]
    local stemplate =
[[$(header)#include "$(regheader)"
//$(declfile)
namespace luabind{
$(lbwrap)
}
namespace $(wpns){
$(wrap)
$(funcdecl)
{
    return
    $(code);
}

}
]]
    local nsname_   = cell:nsname_()
    local funcdecl  = lbind.NS..'::scope reg_'..nsname_..'()'
    local funccall  = lbind.WPNS..'::reg_'..nsname_..'()'
    local regheader = nsname_..'.h'
    local regsource = nsname_..'.cpp'
    local cf        = codefile.new{'sechead', 'secluabind', 'secluagccxml', 'seccode'}
    local status, code, wrap, lbwrap = pcall(cell.gencode, cell)
    if not status then
        fc:clean()
        error(code, 2)
    end
    local repmap    = {
        ['$(declfile)']  = '#include <'..cell:declfile()..'>',
        ['$(macro)']     = string.upper(nsname_..'_'),
        ['$(funcdecl)']  = funcdecl,
        ['$(wpns)']      = lbind.WPNS,
        ['$(header)']    = lbind.HEADER,
        ['$(code)']      = code,
        ['$(wrap)']      = wrap,
		['$(lbwrap)']    = lbwrap,
        ['$(regheader)'] = regheader,
    }
    local hcode = utils.template(htemplate, repmap)
    local scode = utils.template(stemplate, repmap)
    local hpath = concatpath(path, regheader)
    local spath = concatpath(path, regsource)
    --print('header << '..hpath..'>>\n'..hcode)
    --print('source << '..spath..'>>\n'..scode)
    utils.checkwrite(fc, hpath, hcode)
    utils.checkwrite(fc, spath, scode)
    return funccall, regheader
end

function mt:write(fname)
    local head = header(fname,self)
    local src = source(fname,self)
    print("-------------header---------------")
    print(head)
    print("-------------source---------------")
    print(src)
    hfile = io.open(fname..'.h', 'w')
    sfile = io.open(fname..'.cpp', 'w')
    hfile:write(head)
    sfile:write(src)
    hfile:close()
    sfile:close()
end

function mt:split(dirname)
    local hmaintemplate = 
[[#ifndef LUAGCCXML_MAIN_H_
#define LUAGCCXML_MAIN_H_
#include <lua.hpp>
void luagccxml_$(modname)_open(lua_State *L);
#endif
]]
    local smaintemplate = 
[[#include "$(modheader)"
$(regheaders)
void luagccxml_$(modname)_open(lua_State *L)
{
    $(ns)::module(L, "$(modname)")
    [
        $(code)
    ];
}
]]
    local regheaders = strbuf.new()
    local code = strbuf.new()
    local fc = filecleaner.new(dirname)
    local function handler(set, cell)
        if cell.ignore then return end
        if cell:isclass() and not (cell.access and cell.access ~= 'public') then
            local regcode, regheader = writeclass(fc, dirname, cell)
            code = code + regcode
            regheaders = regheaders + ('#include "'..regheader..'"')
        end
    end
    self.codeset:traverse(handler)
    --now generate mod_main.h and mod_main.cpp
    local hpath = utils.concatpath(dirname, self.name..'_main.h')
    local spath = utils.concatpath(dirname, self.name..'_main.cpp')
    local repmap = {
        ['$(modheader)']  = self.name..'_main.h',
        ['$(ns)']         = lbind.NS,
        ['$(modname)']    = self.name,
        ['$(code)']       = code:concat(',\n        '),
        ['$(regheaders)'] = regheaders:concat('\n')..'\n',
    }
    local hcode = utils.template(hmaintemplate, repmap)
    local scode = utils.template(smaintemplate, repmap)
	local luagccxmlhpp = utils.concatpath(dirname, 'luagccxml.hpp')
    --print("main header << "..hpath..' >>')
    --print(hcode)
    --print("main source << "..spath..' >>')
    --print(scode)
    utils.checkwrite(fc, hpath, hcode)
    utils.checkwrite(fc, spath, scode)
	utils.checkwrite(fc, luagccxmlhpp, luagccxml.content)
    fc:clean()
end
